#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <unistd.h>
#include <termios.h>
#include <sys/wait.h>

#include "ysh.h"
#include "encap.h"
#include "error.h"
#include "sig_handle.h"
#include "pool.h"

void *ec_malloc(size_t size)
{
    void *p;
    p = malloc(size);
    if (p != NULL) {
        return p;
    }
    else {
        sprintf(error_message, "Calling malloc failed: \'%s\'\n", strerror(errno));
        error_msg(error_message);
        exit(EXIT_FAILURE);
    }
}

void ec_free(void *p)
{
    free(p);
}

int ec_pipe(int fildes[2])
{
    int ret;
    ret = pipe(fildes);
    if (ret < 0) {
        sprintf(error_message, "Calling pipe failed: \'%s\'\n", strerror(errno));
        error_msg(error_message);
    }
    return ret;
}

int ec_tcsetpgrp(int fildes, pid_t pgid_id)
{
    int ret;

    ret = tcsetpgrp(fildes, pgid_id);
    if (ret < 0) {
        sprintf(error_message, "Calling tcsetpgrp failed: \'%s\'.\n", strerror(errno));
        error_msg(error_message);
    }
    return ret;
}

pid_t ec_tcgetpgrp(int fildes)
{
    int ret;

    ret = tcgetpgrp(fildes);
    if (ret < 0) {
        sprintf(error_message, "Calling tcgetpgrp failed: \'%s\'\n", strerror(errno));
        error_msg(error_message);
    }
    return ret;
}

int ec_kill(pid_t pid, int signo)
{
    int ret;
    
    ret = kill(pid, signo);
    if (ret < 0) {
        sprintf(error_message, "Calling kill failed: \'%s\'\n", strerror(errno));
        error_msg(error_message);
    }
    return ret;
}

char *ec_sig2str(int signo)
{
    return (char*)sys_siglist[signo];
}

pid_t ec_wait(int *stat_loc)
{
    pid_t pid;
    pid = wait(stat_loc);
    if (pid < 0) {
        sprintf(error_message, "Calling wait failed: \'%s\'\n", strerror(errno));
        error_msg(error_message);
    }
    return pid;
}

int ec_waitpid(pid_t pid, int *stat_loc, int options, int fg_or_bg)
{
    pid_t   t_pid;
    int     w_signo;
    Process *t_p;
    Job     *t_j;
    char    *t_s;

    do {
        t_pid = waitpid(pid, stat_loc, options);
        if (t_pid < 0) {
            switch(errno) {
                case ECHILD:
                    return 0;
                case EINTR:
                    continue;
                    break;
                case EINVAL:
                    return t_pid;
                default:
                    break;
            }
        }
        else if (fg_or_bg == WAITPID_BG && t_pid == 0) {
            return t_pid;
        }
        else {
            t_p = find_p(t_pid);
            t_j = t_p->job;
            if (WIFEXITED(*stat_loc)) {
                if (fg_or_bg == WAITPID_FG) {
                    jc_status = WEXITSTATUS(*stat_loc);
                }
                t_s = "DONE";
                if (t_j->jrpn == 1 && t_j->jgid != ec_tcgetpgrp(TERM_FILEDES)) {
                    jc_info(t_j->jid, t_s, t_j->jstring);
                }
                delete_p(t_pid);
            }
            else if (WIFSIGNALED(*stat_loc)) {
                w_signo = WTERMSIG(*stat_loc);
                t_s = ec_sig2str(w_signo);
                if (t_j->jrpn == 1 && t_j->jgid != ec_tcgetpgrp(TERM_FILEDES)) {
                    jc_info(t_j->jid, t_s, t_j->jstring);
                }
                delete_p(t_pid);
            }
            else if (WIFSTOPPED(*stat_loc)) {
                w_signo = WSTOPSIG(*stat_loc);
                t_p->status = JS_STOPPED;
                if (t_j->jrpn == 1 || pool_check_status(t_j->jid, JS_STOPPED) == TRUE) {
                    t_j->jstatus = JS_STOPPED;
                    t_s = ec_sig2str(w_signo);
                }
                jc_info(t_j->jid, t_s, t_j->jstring);
            }
#ifdef WIFCONTINUED
            else if(WIFCONTINUED(*stat_loc)) {
                t_p->status = JS_RUNNING;
                if (t_j->jstatus == JS_STOPPED) {
                    t_j->jstatus = JS_RUNNING;
                    t_s = "CONTINUED";
                    jc_info(t_j->jid, t_s, t_j->jstring);
                }
            }
#endif
            else {
                sprintf(sh_info_str, "Unexpected status 0x%x when waiting process %d.\n", *stat_loc, t_pid);
                sh_info(sh_info_str);
            }
        }
    } while (TRUE);
}

int ec_tcgetattr(int fildes, struct termios *termptr)
{
    int ret;
    ret = tcgetattr(fildes, termptr);
    if (ret < 0) {
        sprintf(error_message, "Calling tcgetattr failed: \'%s\'\n", strerror(errno));
        error_msg(error_message);
    }
    return ret;
}

int ec_tcsetattr(int fildes, int opt, const struct termios *termptr, tcflag_t flag)
{
    int ret;
    struct termios term;

    // Check will be performed outside.
    ret  = tcsetattr(fildes, opt, termptr);
    if (ret < 0) {
        sprintf(error_message, "Calling tcsetattr failed: \'%s\'\n", strerror(errno));
        error_msg(error_message);
    }
    ec_tcgetattr(TERM_FILEDES, &term);
    if ((term.c_lflag & flag) == (tcflag_t)0) {
       sprintf(error_message, "Setting terminal option to 0x%x failed.\n", flag);
       error_msg(error_message);
    }
    return ret;
}

int ec_gethostname(char *name, int namelen)
{
    int ret;

    ret = gethostname(name, namelen);
    if (ret < 0) {
        sprintf(error_message, "Calling gethostname failed: \'%s\'\n", strerror(errno));
        error_msg(error_message);
    }
    return strlen(name);
}

char *ec_getcwd(char *buf, size_t size)
{
    char *ret;
    ret = getcwd(buf, size);
    if (ret == NULL) {
        sprintf(error_message, "Calling getcwd failed: \'%s\'\n", strerror(errno));
        error_msg(error_message);
    }
    return ret;
}

